package com.kili.recommend.data.task;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kili.recommend.data.config.CustomConfig;
import com.kili.recommend.data.dto.CalculateScoreDto;
import com.kili.recommend.data.dto.EventWeight;
import com.kili.recommend.data.entity.Recommend;
import com.kili.recommend.data.entity.User;
import com.kili.recommend.data.service.RecommendService;
import com.kili.recommend.data.service.UserService;
import com.kili.recommend.data.util.FileUtil;
import com.kili.recommend.data.util.S3ClientUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 计算分值
 *
 * @author fenny
 * @since 2019/08/30
 */
@Slf4j
@Component
public class CalculateScoreTask {

    private static boolean FLAG = true;

    @Resource
    private CustomConfig customConfig;

    @Resource
    private UserService userService;

    @Resource
    private RecommendService recommendService;

    @Resource
    private ExecutorService fileAppend;

    private static final List<EventWeight> SHOPPING_CART = new ArrayList<EventWeight>() {{
        add(EventWeight.builder().event("addToShoppingcart").weight(BigDecimal.valueOf(0.4)).build());
        add(EventWeight.builder().event("submitOrderDetail").weight(BigDecimal.valueOf(0.2)).build());
        add(EventWeight.builder().event("payOrderDetail").weight(BigDecimal.valueOf(0.4)).build());
    }};

    /**
     * 计算评分
     */
    @Scheduled(fixedRate = 1000 * 60 * 60)
    public void execute() {
        if (FLAG) {
            File tempFile = null;
            FileWriter writer = null;
            long begin = System.currentTimeMillis();
            FLAG = false;
            try {
                tempFile = File.createTempFile("recommendations", ".txt");
                QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                queryWrapper.select("user_id");
                List<User> users = userService.list(queryWrapper);
                if (users.size() > 0) {
                    CountDownLatch latch = new CountDownLatch(users.size());
                    writer = new FileWriter(tempFile, true);
                    FileWriter finalWriter = writer;
                    users.forEach(user -> {
                        fileAppend.submit(() -> {
                            List<Recommend> recommends = recommendService.
                                    calculateScore(CalculateScoreDto.builder().
                                            weights(SHOPPING_CART).userId(user.getUserId()).build());
                            recommends.forEach(recommend -> {
                                try {
                                    finalWriter.write(String.valueOf(recommend.getUserId()) + "," + recommend.getSkuId() + "," + recommend.getScore() + "\n");
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            });
                            latch.countDown();
                        });
                    });
                    latch.await(1, TimeUnit.HOURS);
                    S3ClientUtils.upload(new FileInputStream(tempFile), customConfig.getRecommendPath());
                }
            } catch (Exception e) {
                log.error("", e);
            } finally {
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (IOException ignored) {
                    }
                }
                if (tempFile != null) {
                    log.info("delete tempFile result={}", tempFile.delete());
                }
                FLAG = true;
                log.info("{}ms", System.currentTimeMillis() - begin);
            }
        }
    }

}
